package com.ks.dao;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.mchange.v2.c3p0.ComboPooledDataSource;

/**
 * @author 47475
 *
 * @param <T> 传入具体的实体类
 */
public abstract class BaseDaoImplWithPool<T> implements BaseDao<T> {
	static Log log = LogFactory.getLog(BaseDaoImplWithPool.class);
	private static ComboPooledDataSource dataSource;
	private Class<T> clazz;

	static {
		try {
			// 加载配置文件，导入一个核心类
			/*
			 * 配置文件中设置的内容不用写其他的是因为它可以自动查找classloader下的c3p0-config.xml文件
			 * （要注意的是文件的名称必须是c3p0-config.xml）如果文件的名称不是这个，将出现错误。
			 */

			dataSource = new ComboPooledDataSource();
		} catch (Exception e) {
			log.error("获取数据源失败", e);
		}

	}

	
	/**
	 * 反射获得实体类对象
	 */
	@SuppressWarnings("unchecked")
	public BaseDaoImplWithPool() {
		clazz = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
	}

	/**获得数据源
	 * @return
	 */
	public static ComboPooledDataSource getDataSource() {
		return dataSource;
	}

	/**提供获得链接
	 * @return
	 * @throws SQLException
	 */
	public static Connection getConnection() throws SQLException {
		return dataSource.getConnection();
	}

	/**关闭资源
	 * @param rs
	 * @param stat
	 * @param conn
	 */
	public void closeAll(ResultSet rs, Statement stat, Connection conn) {
		// 6、关闭一切
		try {
			if (rs != null) {
				rs.close();
			}
			if (stat != null) {
				stat.close();
			}
			if (conn != null) {
				conn.close();
			}
		} catch (SQLException e) {
			log.error("关闭流时发生异常！", e);
		}
	}

	// 关闭一切
	public void closeAll(Statement stat, Connection conn) {
		// 6、关闭一切
		try {
			if (stat != null) {
				stat.close();
			}
			if (conn != null) {
				conn.close();
			}
		} catch (SQLException e) {
			log.error("", e);
		}
	}

	// DML
	@Override
	public int executeUpdate(String sql, Object[] obj) {
		Connection conn = null;
		PreparedStatement stat = null;
		int ret = 0;
		try {
			conn = getConnection();
			// 3、创建传输对象statmemnt
			stat = conn.prepareStatement(sql);// ？不确定:类型、数量
			// 3+、绑定替换数据
			for (int i = 0; i < obj.length; i++) {
				stat.setObject(i + 1, obj[i]);
			}
			// 4、发送sql语句，并且接收返回结果 : DML -> executeUpdate ; DQL -> executeQuery
			ret = stat.executeUpdate();
			// 5、如果返回rs类型的数据，需要将数据转换成list
		} catch (Exception e) {
			log.error("", e);
		} finally {
			closeAll(stat, conn);
		}
		return ret;
	}

	// DQL
	public List<T> executeQuery(String sql, Object[] obj) {
		Connection conn = null;
		PreparedStatement stat = null;
		ResultSet rs = null;
		List<T> list = new ArrayList<>();
		try {
			conn = getConnection();
			// 3、创建传输对象statmemnt
			stat = conn.prepareStatement(sql);// ？不确定:类型、数量
			// 3+、绑定替换数据
			for (int i = 0; i < obj.length; i++) {
				stat.setObject(i + 1, obj[i]);
			}
			// 4、发送sql语句，并且接收返回结果 : DML -> executeUpdate ; DQL -> executeQuery
			rs = stat.executeQuery();
			// 5、如果返回rs类型的数据，需要将数据转换成list
			ResultSetMetaData rsmd = rs.getMetaData();// 列名信息
			int columuCount = rsmd.getColumnCount();// 列的数量
			while (rs.next()) {
				T t = clazz.newInstance();// 创建对象
				for (int i = 0; i < columuCount; i++) {// 封装数据
					Field f = clazz.getDeclaredField(rsmd.getColumnName(i + 1));// 列名->属性名->属性对象
					f.setAccessible(true);
					f.set(t, rs.getObject(i + 1));// 将rs列中的值赋给属性
				}
				list.add(t);
			}
		} catch (Exception e) {
			log.error("", e);
		} finally {
			closeAll(rs, stat, conn);
		}
		return list;
	}

	// DQL
	@Override
	public List<T> executeQuery(String sql) {
		Connection conn = null;
		PreparedStatement stat = null;
		ResultSet rs = null;
		List<T> list = new ArrayList<>();
		try {
			conn = getConnection();
			// 3、创建传输对象statmemnt
			// ？不确定:类型、数量
			stat = conn.prepareStatement(sql);
			// 3+、绑定替换数据
			// 4、发送sql语句，并且接收返回结果 : DML -> executeUpdate ; DQL -> executeQuery
			rs = stat.executeQuery();
			// 5、如果返回rs类型的数据，需要将数据转换成list
			// 列名信息
			ResultSetMetaData rsmd = rs.getMetaData();
			// 列的数量
			int columuCount = rsmd.getColumnCount();
			while (rs.next()) {
				// 创建对象
				T t = clazz.newInstance();
				// 封装数据
				for (int i = 0; i < columuCount; i++) {
					Field f = clazz.getDeclaredField(rsmd.getColumnName(i + 1));// 列名->属性名->属性对象
					f.setAccessible(true);
					// 将rs列中的值赋给属性
					f.set(t, rs.getObject(i + 1));
				}
				list.add(t);
			}
		} catch (Exception e) {
			log.error("", e);
		} finally {
			closeAll(rs, stat, conn);
		}
		return list;
	}

	@Override
	public int executeQueryCount(String sql) {
		Connection conn = null;
		PreparedStatement stat = null;
		ResultSet rs = null;
		int t = 0;
		try {
			conn = getConnection();
			// 3、创建传输对象statmemnt
			stat = conn.prepareStatement(sql);// ？不确定:类型、数量
			// 3+、绑定替换数据
			// 4、发送sql语句，并且接收返回结果 : DML -> executeUpdate ; DQL -> executeQuery
			rs = stat.executeQuery();
			rs.next();
			t = rs.getInt(1);
			System.out.println(t);
		} catch (Exception e) {
			log.error("", e);
		} finally {
			closeAll(rs, stat, conn);
		}
		return t;
	}
}
